package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.guigu.common.result.ResultCodeEnum;
import com.atguigu.guigu.common.utils.Assert;
import com.atguigu.service.base.utils.JwtUtils;
import com.atguigu.service.base.utils.LendNoUtils;
import com.atguigu.srb.core.hfb.CoreUtils;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendReturnMapper;
import com.atguigu.srb.core.service.*;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 还款记录表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-11-19
 */
@Service
@Slf4j
public class LendReturnServiceImpl extends ServiceImpl<LendReturnMapper, LendReturn> implements LendReturnService {
    @Resource
    LendService lendService;
    @Resource
    UserInfoService userInfoService;
    @Resource
    UserAccountService userAccountService;
    @Resource
    LendItemReturnService lendItemReturnService;
    @Resource
    LendItemService lendItemService;
    @Resource
    TransFlowService transFlowService;
    public List<LendReturn> getLendReturns(Lend lend) {
        //获取标的的还款方式的 每一期还款本金和利息的 map
        Map<Integer, BigDecimal> perMonthPrincipal = null;
        Map<Integer, BigDecimal> perMonthInterest = null;
        CoreUtils.initPerMonthMap(perMonthPrincipal,perMonthInterest,lend.getInvestAmount() , lend.getLendYearRate(),lend.getPeriod(),lend.getReturnMethod());
        //借款人只有一个：为他的一个借款生成还款期数个 还款计划
        //根据标的期数遍历，生成期数对应个 还款计划
        //特例：如果还款方式是一次性还清本息  只有一期============//TODO
        List<LendReturn> lendReturns = new ArrayList<>();
        if(lend.getReturnMethod()==4){
            LendReturn lendReturn = getLendReturn(lend, perMonthPrincipal, perMonthInterest, lend.getPeriod());
            lendReturns.add(lendReturn);
        }else{
            for (Integer i = 1; i <= lend.getPeriod(); i++) {
                LendReturn lendReturn = getLendReturn(lend, perMonthPrincipal, perMonthInterest, i);
//                lendReturnService.save(lendReturn);
                lendReturns.add(lendReturn);
            }
        }
        //批量保存还款计划
        this.saveBatch(lendReturns);//保存时每个lendReturn都会生成id
        return lendReturns;
    }
    //构建访问hfb还款接口自动提交的表单
    @Override
    public String repayment(String id, String token) {
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = userInfoService.checkStatus(userId);
        LendReturn lendReturn = this.getById(id);
        Long lendId = lendReturn.getLendId();
        Lend lend = lendService.getById(lendId);
        /*
            1、判断还款人对不对、状态是不是出于还款中的状态
            2、判断还款人账户余额是否足够
         */
        Assert.notTrue(lendReturn.getUserId().longValue()==userId.longValue() , ResultCodeEnum.FETCH_USERINFO_ERROR);
        Assert.notTrue(lendReturn.getStatus()==0 , ResultCodeEnum.LEND_FULL_SCALE_ERROR);
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));
        Assert.notTrue(userAccount.getAmount().doubleValue()
                        >=lendReturn.getTotal().doubleValue() ,
                ResultCodeEnum.NOT_SUFFICIENT_FUNDS_ERROR);
        //准备hfb放款接口需要的参数
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        //还款商品的标题
        params.put("agentGoodsName",lend.getTitle());
        //本次还款的编号
//        String no = LendNoUtils.getNo();
        params.put("agentBatchNo",lendReturn.getReturnNo());//使用LendReturn的no
        //还款人账户bindCode
        params.put("fromBindCode",userInfo.getBindCode());
        //本期还款总额
        params.put("totalAmt",lendReturn.getTotal());
        params.put("note",lend.getTitle()+"第期"+lendReturn.getCurrentPeriod()+"还款");
        //本期投资人回款列表: 每个投资人的回款信息 使用map保存，所有的回款信息 存到一个List集合中转为json字符串
        //查询本次还款对应的所有的回款计划
        List<LendItemReturn> lendItemReturns = lendItemReturnService.list(Wrappers.lambdaQuery(LendItemReturn.class)
                .eq(LendItemReturn::getLendReturnId, id));

        List<Map<String, Object>> list = lendItemReturns.stream().map(lendItemReturn -> {
            //lendItem：投资项
            LendItem lendItem = lendItemService.getById(lendItemReturn.getLendItemId());
            UserInfo invertUserInfo = userInfoService.getById(lendItemReturn.getInvestUserId());
            Map<String, Object> map = new HashMap<>();
            //本期回款所属的标的编号(必须保证hfb可以找到对应的标的)
            map.put("agentProjectCode",lend.getLendNo());
            //投资单号:
            map.put("voteBillNo",lendItem.getLendItemNo());
            //回款人账户
            map.put("toBindCode",invertUserInfo.getBindCode());
            //还款金额
            map.put("transitAmt",lendItemReturn.getTotal());
            //本金
            map.put("baseAmt",lendItemReturn.getPrincipal());
            //利息
            map.put("benifitAmt",lendItemReturn.getInterest());
            //手续费： 在收益基础上扣除
            map.put("feeAmt",lendItemReturn.getInterest().multiply(new BigDecimal("0.05")));
            return map;
        }).collect(Collectors.toList());
        params.put("data", JSON.toJSONString(list));
        //手续费
        params.put("voteFeeAmt",new BigDecimal("0"));
        params.put("returnUrl",HfbConst.BORROW_RETURN_RETURN_URL);
        params.put("notifyUrl",HfbConst.BORROW_RETURN_NOTIFY_URL);
        params.put("timestamp",System.currentTimeMillis());
        params.put("sign",RequestHelper.getSign(params));
        //构建自动提交的表单字符串
        String formStr = FormHelper.buildForm(HfbConst.BORROW_RETURN_URL,params);

        //保存日志
        TransFlow transFlow2 = new TransFlow();
        transFlow2.setUserId(userId);
        transFlow2.setUserName(userInfo.getName());
        transFlow2.setTransNo(lendReturn.getReturnNo());//直接保存成功的日志 无需回调来更新
        //（1：充值 2：提现 3：投标 4：投资回款 5: 账户入账 6:投资扣减.. .  7、用户还款）
        transFlow2.setTransType(7);
        transFlow2.setTransTypeName("还款");
        transFlow2.setTransAmount(lendReturn.getTotal());
        transFlow2.setStatus(0);
        transFlowService.save(transFlow2);
        return formStr;
    }

    @Override
    public String repaymentNotify(HttpServletRequest request) {
        //1、获取参数
        Map<String, String[]> map = request.getParameterMap();
        Map<String, Object> params = RequestHelper.switchMap(map);
        //2、验证签名
        boolean b = RequestHelper.isSignEquals(params);
        if(!b){
            log.error("还款回调失败，签名错误{}" , JSON.toJSONString(params));
            return "fail";
        }
        //3、验证状态码
        //agentBatchNo解决幂等性
        String agentBatchNo = params.get("agentBatchNo").toString();
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBatchNo));
        if(transFlow==null || transFlow.getStatus()!=0){
            log.error("还款重复回调：{}" , JSON.toJSONString(params));
            return "fail";
        }
        String resultCode = params.get("resultCode").toString();
        if(!"0001".equals(resultCode)){
            log.error("还款回调失败，状态码错误{}" , JSON.toJSONString(params));
            transFlow.setStatus(-1);
            transFlow.setMemo(JSON.toJSONString(params));
            transFlowService.updateById(transFlow);
            return "fail";
        }
        //4、判断扣款金额是否正确
        BigDecimal totalAmt = new BigDecimal(params.get("totalAmt").toString());
        if(transFlow.getTransAmount().doubleValue()!=totalAmt.doubleValue()){
            log.error("还款回调失败，还款金额错误：{}" , JSON.toJSONString(params));
            transFlow.setStatus(-1);
            transFlow.setMemo(JSON.toJSONString(params));
            transFlowService.updateById(transFlow);
            return "fail";
        }

        //还款成功：
        transFlow.setStatus(1);
        transFlow.setMemo(JSON.toJSONString(params));
        transFlowService.updateById(transFlow);
        //1、根据批次号查询 还款记录:agentBatchNo   更新还款记录状态
        LendReturn lendReturn = this.getOne(Wrappers.lambdaQuery(LendReturn.class)
                .eq(LendReturn::getReturnNo, agentBatchNo));
        lendReturn.setStatus(1);
        lendReturn.setRealReturnTime(new Date());
        //是否逾期：
        long time = lendReturn.getReturnDate().getTime();
        long currentTime = new Date().getTime();

        lendReturn.setOverdue(currentTime>time);
        //如果逾期：  (currentTime-time)转为天数，使用还款本金利息*天数*逾期时一天的利率
        lendReturn.setOverdueTotal(new BigDecimal("0"));
        this.updateById(lendReturn);

        //2、更新还款人账户余额
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, lendReturn.getUserId()));
        userAccount.setAmount(userAccount.getAmount().subtract(totalAmt));
        userAccountService.updateById(userAccount);
        UserInfo userInfo = userInfoService.getById(lendReturn.getUserId());

        //3、更新投资人投资项 回款记录的状态+ 账户余额
        List<LendItemReturn> lendItemReturns = lendItemReturnService
                .list(Wrappers.lambdaQuery(LendItemReturn.class)
                        .eq(LendItemReturn::getLendReturnId, lendReturn.getId()));
        lendItemReturns.forEach(lendItemReturn -> {
            //设置回款项状态为已回款
            lendItemReturn.setOverdue(lendReturn.getOverdue());
            //计算
            lendItemReturn.setOverdueTotal(new BigDecimal("0"));
            lendItemReturn.setRealReturnTime(lendReturn.getRealReturnTime());
            lendItemReturn.setStatus(1);
            //修改账户余额 加上回款总金额
            Long investUserId = lendItemReturn.getInvestUserId();
            UserAccount account = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                    .eq(UserAccount::getUserId, investUserId));
            //扣除了部分收益
            BigDecimal returnAmount = lendItemReturn.getTotal().subtract(lendItemReturn.getInterest()
                    .multiply(new BigDecimal("0.05")));
            BigDecimal total = account.getAmount().add(returnAmount);
            account.setAmount(total);
            userAccountService.updateById(account);
            UserInfo userInfo2 = userInfoService.getById(investUserId);
            TransFlow transFlow2 = new TransFlow();
            transFlow2.setUserId(investUserId);
            transFlow2.setUserName(userInfo2.getName());
            transFlow2.setTransNo(LendNoUtils.getNo());//直接保存成功的日志 无需回调来更新
            //（1：充值 2：提现 3：投标 4：投资回款 5: 账户入账 6:投资扣减.. .  7、用户还款）
            transFlow2.setTransType(4);
            transFlow2.setTransTypeName("回款");
            transFlow2.setTransAmount(returnAmount);
            transFlow2.setStatus(1);
            transFlowService.save(transFlow2);

        });
        lendItemReturnService.updateBatchById(lendItemReturns);

        return "success";
    }

    //生成还款计划
    public LendReturn getLendReturn(Lend lend, Map<Integer, BigDecimal> perMonthPrincipal, Map<Integer, BigDecimal> perMonthInterest, Integer i) {
        LendReturn lendReturn = new LendReturn();
        lendReturn.setLendId(lend.getId());
        lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
        lendReturn.setReturnNo(LendNoUtils.getReturnNo());//还款计划编号
        lendReturn.setUserId(lend.getUserId());
        lendReturn.setAmount(lend.getAmount());//标的目标金额
        lendReturn.setBaseAmount(lend.getInvestAmount());//借款人募资到的金额： 用来计算利息
        lendReturn.setCurrentPeriod(i);
        lendReturn.setLendYearRate(lend.getLendYearRate());
        lendReturn.setReturnMethod(lend.getReturnMethod());
        //TODO:   直接计算本金利息 有可能会和  回款计划有误差(可以等 回款计划生成后 累加所有回款计划当前期的本金利息更新)
        //根据还款方式计算本金
//        lendReturn.setPrincipal(perMonthPrincipal.get(i));//本期还款的本金
//        lendReturn.setInterest(perMonthInterest.get(i));//本期还款的利息
//        lendReturn.setTotal(lendReturn.getPrincipal().add(lendReturn.getInterest()));//本期还款的本金+利息
        lendReturn.setFee(new BigDecimal("0"));//手续费
        //lend的起息日+期数
        //本期还款时间
        lendReturn.setReturnDate(new DateTime(lend.getLendStartDate()).plusMonths(i).toDate());
        lendReturn.setRealReturnTime(null);
        lendReturn.setOverdue(null);
        lendReturn.setOverdueTotal(null);
        if (i == lend.getPeriod()) {
            lendReturn.setLast(true);
        } else {
            lendReturn.setLast(false);
        }
        lendReturn.setStatus(0);//0未归还
        return lendReturn;
    }

}
